Serverless Framework + Lambda(Python) を使ってChatworkにCloudWatchのアラートを投稿
どうも!AWS歴、数週間の西村祐二@大阪です。
AWSの勉強のために
EC2で発生した障害をCloudWatchで検知、監視結果をLambdaのトリガーに設定したSNSを経由して
Chatworkに投稿するというのをserverless frameworkを使ってやってみます。
あまりAWSに触れたことのない方の参考になれば幸いです。
概要図
ゴール
障害検知したらChatworkにアラート通知が来るようにします。
事前準備
Serverless Frameworkの環境構築
以下のページがとても参考になります。
ChatworkのAPIトークン、ルームIDの取得
以下のページがとても参考になります。
環境
Serverless Framework 1.12.1
EC2 t2.micro Amazon Linux
Serverless Frameworkを用いたデプロイ
Serverless Frameworkを使って、外部モジュール等を含めたLambda関数のアップロードなど行います。
サービス(作業ディレクトリ)を作成
$ sls create -t aws-python3 -p chatwork-cloudwatch Serverless: Generating boilerplate... Serverless: Generating boilerplate in "/Users/nishimura.yuji/work/lambda/blog/chatwork-cloudwatch" _______ __ | _ .-----.----.--.--.-----.----| .-----.-----.-----. | |___| -__| _| | | -__| _| | -__|__ --|__ --| |____ |_____|__| \___/|_____|__| |__|_____|_____|_____| | | | The Serverless Application Framework | | serverless.com, v1.12.1 -------' Serverless: Successfully generated boilerplate for template: "aws-python3"
サービス名は「chatwork-cloudwatch」として作成し、
「aws-python3」と指定することでpython3.6でサービスが作成されます。
完了後、以下ファイルが作成されます。
$ cd chatwork-cloudwatch $ tree . . ├── handler.py └── serverless.yml 0 directories, 2 files
serverless.ymlの設定
デプロイ時に設定したい項目はserverless.ymlを編集することで設定できます。
今回は下記のように設定しました。
コメント部分は省いております。
service: chatwork-cloudwatch provider: name: aws runtime: python3.6 region: ap-northeast-1 profile: sls environment: CHATWORK_API_KEY: xxxxxxxxxxxxxxxxxxxxxx CHATWORK_HEADER: X-ChatWorkToken CHATWORK_ROOM_ID: xxxxxxxxxxxxx CHATWORK_URL: https://api.chatwork.com/v2 plugins: - serverless-python-requirements functions: dispatcher: # file_name handler: dispatcher.dispatch # file_name.functions_name events: - sns: sls-cloudwatch # topics_name
各ブロックごとに説明
サービ作成時に設定した名前が設定されます。
service: chatwork-cloudwatch
サービス作成時に 「aws-python3」とするとpython3.6が設定されます。
regionは初期設定だと「us-east-1」になっているので、「ap-northeast-1」の東京リージョンに変更しておきます。
使いたいprofileも設定できます。
provider: name: aws runtime: python3.6 region: ap-northeast-1 profile: sls
デプロイしたときに環境変数として設定できます。
APIキーなどはプログラムの中に直書きするのが嫌だったので、こちらに記載しました。
セキュリティを高めるのならKMSで暗号化して、Lambda実行時に復号化するのがよいかと思います。
KMSの設定については今回は割愛させていただきます。
environment: CHATWORK_API_KEY: xxxxxxxxxxxxxxxxxxxxxx CHATWORK_HEADER: X-ChatWorkToken CHATWORK_ROOM_ID: xxxxxxxxxxxxx CHATWORK_URL: https://api.chatwork.com/v2
外部モジュールを含めてデプロイするときの設定です。
plugins: - serverless-python-requirements
以下のページがとても参考になります。
デプロイすると自動的にSNSをトリガーとして設定してくれます。
今回トピックス名は「sls-cloudwatch」として設定しております。
公式のドキュメントを参考に設定したので、はじめに作成されたものから、
ファイル名を「handler.py」→「dispatcher.py」に、
関数名を「hello」→「dispatch」に変更する必要があります。
公式ドキュメント https://serverless.com/framework/docs/providers/aws/events/sns/
functions: dispatcher: # file_name handler: dispatcher.dispatch # file_name.functions_name events: - sns: sls-cloudwatch # topics_name
PythonでLambda関数作成
$ cp -ip handler.py dispatcher.py $ vi dispatcher.py import json import logging import requirements import requests import urllib import os logger = logging.getLogger() logger.setLevel(logging.INFO) message_header = "============ Alart ===========\n" def dispatch(event, context): logger.info("Event: " + str(event)) message = json.loads(event['Records'][0]['Sns']['Message']) logger.info("Message: " + str(message)) # 環境変数から情報取得 CHATWORK_API_KEY = os.environ['CHATWORK_API_KEY'] CHATWORK_HEADER = os.environ['CHATWORK_HEADER'] CHATWORK_ROOM_ID = os.environ['CHATWORK_ROOM_ID'] CHATWORK_URL = os.environ['CHATWORK_URL'] # Chatwork APIを利用するためのURL URL = '{0}/rooms/{1}/messages'.format(CHATWORK_URL,CHATWORK_ROOM_ID) # メッセージ取得 chatwork_message = get_cloudwatch_message(message) payload = {'body': chatwork_message} headers = {CHATWORK_HEADER: CHATWORK_API_KEY} try: # Chatworkに投稿 requests.post(URL, headers=headers, params=payload) except: logger.info("Failed! Message : \n" + str(chatwork_message)) else: logger.info("Success! Message posted to: \n" + str(chatwork_message)) def get_cloudwatch_message(data): logger.info("get data: " + str(data)) # 渡ってきたデータからほしい情報を抽出 alarm_name = data['AlarmName'] new_state = data['NewStateValue'] reason = data['NewStateReason'] metric_name = data['Trigger']['MetricName'] target_name = data['Trigger']['Dimensions'][0]['name'] target_value = data['Trigger']['Dimensions'][0]['value'] # Chatworkに投稿したいメッセージ chatwork_message = '{0}\n\ アラーム名:{1} \n\ new_state:{2} \n\ reason:{3} \n\ metric_name:{4} \n\ target_name:{5} \n\ target_value:{6}'.format(message_header,alarm_name,new_state,reason,metric_name,target_name,target_value) return chatwork_message
参考:SNSからLambdaに渡ってくるデータの例(eventに入ってくるデータ)
{ 'Records': [ { 'EventSource': 'aws:sns', 'EventVersion': '1.0', 'EventSubscriptionArn': 'arn:aws:sns:ap-northeast-1:xxxxxxxxxx', 'Sns': { 'Type': 'Notification', 'MessageId': 'xxxxxxxxxx', 'TopicArn': 'arn:aws:sns:ap-northeast-1:xxxxxxxxxx', 'Subject': 'INSUFFICIENT_DATA: "xxxxxxxxxx" in Asia Pacific - Tokyo', 'Message': ' { "AlarmName":"xxxxxxxxxx", "AlarmDescription":"Created from EC2 Console", "AWSAccountId":"xxxxxxxxxx", "NewStateValue":"INSUFFICIENT_DATA", "NewStateReason":"Insufficient Data: 1 datapoint was unknown.", "StateChangeTime":"2017-05-12Txx:xx:xx.573+0000", "Region":"Asia Pacific - Tokyo", "OldStateValue":"OK", "Trigger": { "MetricName":"xxxxxxxxxx", "Namespace":"AWS/EC2", "StatisticType":"Statistic", "Statistic":"MAXIMUM", "Unit":null, "Dimensions":[ { "name":"InstanceId", "value":"i-xxxxxxxxxx" }], "Period":60, "EvaluationPeriods":1, "ComparisonOperator":"GreaterThanThreshold","Threshold":5.0, "TreatMissingData":"","EvaluateLowSampleCountPercentile":"" } }', 'Timestamp': '2017-05-12Txx:xx:xx.612Z', 'SignatureVersion': '1', 'Signature': ・・・・
外部モジュールを含めてデプロイするための準備
外部モジュールを含めてデプロイするために
「requirements.txt」「requirements.py」を作成します。
以下のページがとても参考になります。
$ vi requirements.txt requests
$ vi requirements.py import os import sys requirements = os.path.join( os.path.split(__file__)[0], '.requirements', ) if requirements not in sys.path: sys.path.append(requirements)
デプロイ
下記コマンドにて外部モジュール含めたデプロイができます。
$ sls deploy -v
Lambdaの実行ロール
Lambdaから他AWSリソースにアクセスしないため既存のロールで問題ないかと思います。
マネージメントコンソールにてデプロイ状況確認
Lambda関数の箇所に「chatwork-cloudwatch-dev-dispatcher」というLambda関数が作成されています。
「chatwork-cloudwatch-dev-dispatcher」を選択し環境変数を確認すると
設定したとおりの値が入っていることがわかります。
トリガー設定もSNSが設定されており、トピックス名が「sls-cloudwatch」とserverless.ymlで設定したものが作成・設定されていることがわかります。
EC2にてインスタンスステータスのCloudWatchアラーム設定
EC2の「モニタリング」タブからアラームの作成ボタンを
クリックするとアラームの編集ウインドウが表示されます。
そこで、通知の送信先を「sls-cloudwatch」、
アラーム検知の条件を「ステータスチェックに失敗(インスタンス)」、
アラーム名を「status_check」として設定し保存します。
意図的に障害発生
下記、ページを参考にEC2にてインターフェイスをダウンさせる
EC2にログイン $ sudo ifconfig eth0 down
しばらく待つと
アラームのステータスが「OK」→「アラーム」に変化します。
Chatworkに通知
うまく動作すると下記のようにLambdaによって、アラート内容をChatworkに通知してくれます。
まとめ
いかがだったでしょうか。
Serverless Framework + Lambda(Python) を使ってCloudWatchのアラートをChatworkに投稿する仕組みを作ってみました。
Lambdaを使って何かをやろうと思うと付随して他のAWSサービスを利用することになるので
とても勉強になりました。
あまりAWSに触れたことがない方のAWSに触れるきっかけや参考になれば幸いです。
また、SNSからどのようなデータが渡ってくるか分かれば、安価で様々な監視ができる仕組みが作れるかと思います。
今回は予め用意されている監視設定を利用しましたが、
次はカスタムメトリクスを使って検知した内容を通知する仕組みを作成したいと思います。